﻿using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService;

/// <summary>
///     通过遍历语法树 <see cref="SyntaxTree" /> 的子节点，
///     声明类型符号、绑定符号类型并进行静态类型检查。
///     <list type="bullet">
///         <item>
///             脚本语法本身不支持生成类型，类型声明应当写在后缀名为.d.mrs
///             的文件中，并且，声明文件中不支持常规的脚本语法。
///         </item>
///         <item>
///             元数据字段定义应当写在.dms扩展名的文件中，其他扩展名的文件需要在
///             文件开头添加格式为 ' &lt;metadata/&gt; 的注释指令
///         </item>
///     </list>
/// </summary>
public abstract class Binder(WorkFolder workFolder)
{
    private DiagnosticStorage? _diagnosticStorage;

    protected WorkFolder WorkFolder { get; } = workFolder;

    
    /// <summary>
    ///     在绑定单个语法树完成时调用此方法，在一次性绑定多个语法树时，
    ///     每一个语法树绑定完成后都会调用此方法。
    /// </summary>
    protected virtual void OnBindSingleTreeCompletedOverride()
    {
    }


    private bool IgnoreTypeError(ITextNode node)
    {
        return WorkFolder.IgnoreTypeError ||
               (node as SyntaxNode)?.GetFlags(SyntaxNodeFlags.IgnoreTypeError) == true;
    }


    /// <summary>
    ///     绑定单个语法树的方法，
    /// </summary>
    /// <param name="syntaxTree">带绑定的语法树</param>
    protected abstract AnalysisTree BindTreeCore(SyntaxTree syntaxTree);


    /// <summary>
    ///     在绑定语法树完成时调用此方法。当一次性绑定多个语法树时，在所有语法树都绑定完成后才会调用此方法。
    /// </summary>
    protected virtual void OnBindCompletedOverride()
    {
    }


    /// <summary>
    ///     绑定指定语法树中的语法节点到符号表。
    /// </summary>
    /// <param name="syntaxTree">已有的语法树</param>
    public AnalysisTree Bind(SyntaxTree syntaxTree)
    {
        _diagnosticStorage = syntaxTree.GetDiagnosticStorage();
        AnalysisTree tree = BindTreeCore(syntaxTree);
        OnBindSingleTreeCompletedOverride();
        OnBindCompletedOverride();
        return tree;
    }


    /// <summary>
    ///     绑定整个列表中的所有语法树。每个语法树使用各自的符号表。
    /// </summary>
    /// <param name="syntaxTrees">需要绑定的语法树列表</param>
    public void Bind(IEnumerable<SyntaxTree> syntaxTrees)
    {
        foreach (SyntaxTree? tree in syntaxTrees)
        {
            _diagnosticStorage = tree.GetDiagnosticStorage();
            BindTreeCore(tree);
            OnBindSingleTreeCompletedOverride();
        }

        OnBindCompletedOverride();
    }


    /// <summary>
    ///     在指定的语法节点上报告错误。
    /// </summary>
    /// <param name="diagnostic">错误信息</param>
    /// <param name="node">报告节点位置</param>
    /// <param name="args">错误信息模板参数</param>
    protected void Raise(DiagnosticDescriptor diagnostic, ITextNode node, params object?[] args)
    {
        if (!IgnoreTypeError(node))
        {
            _diagnosticStorage?.Raise(diagnostic, node, args);
        }
    }
}